// Copyright (c) 2021 Presto Labs Pte. Ltd.
// Author: gekim
#pragma once

#include <boost/date_time/posix_time/posix_time.hpp>
#include <iostream>
#include <memory>
#include <optional>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include <regex>

#include "coin2/exchange/base/symbology/derivative_util.h"
#include "coin2/exchange/base/symbology/spot_product.h"
#include "coin2/exchange/bybit/symbology/currency.h"

namespace coin2::exchange::bybit::symbology {
using ExchangeType = base::market::ExchangeType;
using coin2::exchange::bybit::symbology::BybitCurrency;

class BybitProduct
    : public base::symbology::SpotProduct<BybitCurrency, BybitProduct>,
      public base::symbology::ProductFromStrMixin<BybitProduct> {
 public:
  typedef SpotProduct<BybitCurrency, BybitProduct> BaseProduct;
  using ContractType = base::symbology::ContractType;

 public:
  typedef struct {
    bool success;
    std::unique_ptr<BybitCurrency> base;
    std::unique_ptr<BybitCurrency> quote;
  } result_t;

  BybitProduct(const BybitCurrency& base, const BybitCurrency& quote)
      : SpotProduct(base, quote, ToBybitNativeProduct(base, quote),
                    ToBybitNativeProduct(base, quote),
                    base::market::ExchangeType::Bybit) {}

 public:
  static std::unique_ptr<BybitProduct> FromNativeStrImpl(
      const std::string& native_product, int64_t resolve_timestamp) {
    auto [success, base, quote] =
        ParseBybitNativeProduct(native_product, resolve_timestamp);
    if (success) {
      return std::make_unique<BybitProduct>(*base, *quote);
    } else {
      return {};
    }
  }

  static result_t ParseBybitNativeProduct(const std::string& product_str,
                                          int64_t resolve_timestamp) {

    result_t result;
    std::string base_str;
    std::string quote_str;

    static const std::regex reg("([A-Z0-9]{1,12})(BTC|USDC|USDT|DAI|EUR|BRZ)");
    std::smatch match;
    bool res = std::regex_match(product_str, match, reg);
    if (!res) {
      result.success = false;
      return result;
    }

    std::string base = match[1];
    std::string quote = match[2];

    result.success = true;
    result.base = BybitCurrency::FromNativeStrNoThrow(base, resolve_timestamp);
    result.quote = BybitCurrency::FromNativeStrNoThrow(quote, resolve_timestamp);
    return result;
  }

  static std::string ToBybitNativeProduct(const BybitCurrency& base,
                                          const BybitCurrency& quote) {
    using base::market::MarketType;
    return base.native_currency(MarketType::Spot) + quote.native_currency(MarketType::Spot);
  }
};

}  // namespace coin2::exchange::bybit::symbology
